﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SocialPlatforms;
using ZerAx.Net;
using ZerAx.ObjectNotation;
using ZerAx.Tweaks;

namespace BlockOS.Client
{
    public abstract class BaseWorld : IWorld
    {
        protected int entityId;
        protected EntityDataStorage entityStorage;
        
        protected Dictionary<Vector3Int, ChunkData> cachedChunks = new();

        public abstract IConnection GetConnection();
        public virtual IEnumerator Init(Action<string> logger)
        {
            var local = GetConnection();
            logger("Sending login message...");

            local.Send(new Packet((byte)ProtocolId.LOGIN).WriteData(w => w.Write(Settings.PLAYER_NAME)));

            bool success = false;
            while (!success)
            {
                local.Update();
                while (local.Poll(out var packet))
                {
                    if ((ProtocolId)packet.typeId == ProtocolId.RESPONSE_LOGIN)
                    {
                        var dom = packet.ReadData(Zon.Deserialze).AsCompound();
                        if (Settings.PLAYER_NAME == dom.GetString("name"))
                        {
                            entityId = dom.GetI32("entityId").Value;
                            success = true;

                            break;
                        }
                    }
                }
                yield return null;
            }
            logger($"Fine, your id is {entityId}. Entering world...");
        }

        public virtual void Deinit()
        {
            GetConnection().Disconnect();
        }

        public ReqChunkReturnCode RequestChunk(int x, int y, int z, out ChunkData chunk)
        {
            chunk = null;
            GetConnection().Send(ProtocolId.REQUEST_CHUNK.CreatePacket().WriteData(Zon.Vec3i(x, y, z).Serialize));
            if (cachedChunks.TryGetValue(new Vector3Int(x, y, z), out var ch))
            {  
                if (ch == null)
                {
                    return ReqChunkReturnCode.NoSuchChunk;
                }
                return ReqChunkReturnCode.Success;
            }
            else
            {
                return ReqChunkReturnCode.NotLoadedYet;
            }
        }

        public (int, EntityData)[] RequestNearbyEntities()
        {
            return entityStorage.Entities;
        }

        public virtual void Update()
        {
            var local = GetConnection();
            local.Update();
            while (local.Poll(out var packet))
            {
                switch ((ProtocolId)packet.typeId)
                {
                    case ProtocolId.RESPONSE_ENTITIES:
                        LoadEntityDatas(packet);
                        break;
                    case ProtocolId.RESPONSE_CHUNK:
                        LoadChunkData(packet);
                        break;
                }
            }
        }

        public void UploadPlayerTransform(Vector3 playerPosition, Vector3 playerRotation)
        {
            GetConnection().Send(new Packet((byte)ProtocolId.REQUEST_ENTITIES)
                .WriteData(Zon.Compound(
                    ("name", Settings.PLAYER_NAME), 
                    ("pos", Zon.Vec3(playerPosition.x, playerPosition.y, playerPosition.z)),
                    ("rot", Zon.Vec3(playerRotation.x, playerRotation.y, playerRotation.z))).Serialize));
        }

        protected void LoadChunkData(Packet packet)
        {
            var chunkPacket = packet.ReadData(Zon.Deserialze) as ZonCompound;
            var pos = chunkPacket.GetVec3i("pos").Value.ToVec();
            var chunk = ChunkData.LoadFromZonDocument(chunkPacket.GetCompound("chunk"));
            
        }


        protected void LoadEntityDatas(Packet packet)
        {   // 数据包中的二进制串序列化实体
            var entities = packet.ReadData(Zon.Deserialze) as ZonList;
            foreach (var entity in entities.data.Select(x => x.AsCompound()))
            {
                var optId = entity.GetI32("entityId");
                if (optId is not int id) continue;
                if (id == entityId) continue;
                if (entityStorage.entities.TryGetValue(id, out var data)) 
                {   // 将对应实体字段设置为最新数据
                    data.SetFromZon(entity);
                }
                else
                {   // 没有该ID对应的实体，向库存中添加新实体
                    entityStorage.entities.Add(id, new EntityData(entity));
                }
            }
        }
    }
}
